/*
 * DataObjectImpl.cpp
 *
 *  Created on: 2012-9-1
 *      Author: yangentao@gmail.com
 */

#include "DataObjectImpl.h"
#include "GlobalMem.h"

#include "EnumFormatEtc.h"
#include "../win32.h"

namespace yet{

void makeFormatEtc(FORMATETC* fe, bool unicode) {
	fe->cfFormat = unicode ? CF_UNICODETEXT : CF_TEXT;
	fe->ptd = 0;
	fe->dwAspect = DVASPECT_CONTENT;
	fe->lindex = -1;
	fe->tymed = TYMED_HGLOBAL;
}

DataObjectImpl::DataObjectImpl() {
	refCount = 1;
	formats = 0;
	stgs = 0;
	length = 0;
}
DataObjectImpl::DataObjectImpl(FORMATETC * formats_, STGMEDIUM *stgs_, int count) {
	refCount = 1;
	length = count;
	formats = new FORMATETC[length];
	stgs = new STGMEDIUM[length];
	for (int i = 0; i < length; ++i) {
		formats[i] = formats_[i];
		stgs[i] = stgs_[i];
	}
}

DataObjectImpl::~DataObjectImpl() {
	if (formats) {
		delete[] formats;
	}
	formats = 0;
	if (stgs) {
		delete[] stgs;
	}
	stgs = 0;
}
void DataObjectImpl::ReleaseStgMediums(){
	if(stgs){
		for(int i=0; i< length; ++i){
			ReleaseStgMedium(&stgs[i]);
			stgs[i].hGlobal = 0;
		}
	}
}
ULONG __stdcall DataObjectImpl::AddRef(void) {
	return InterlockedIncrement(&refCount);
}

ULONG __stdcall DataObjectImpl::Release(void) {
	LONG r = InterlockedDecrement(&refCount);
	if (r == 0) {
		ReleaseStgMediums();
		delete this;
		return 0;
	} else {
		return r;
	}
}
HRESULT __stdcall DataObjectImpl::QueryInterface(REFIID riid, PVOID *ppv) {
	*ppv = 0;
	if (riid == IID_IUnknown || riid == IID_IDataObject) {
		*ppv = this;
		AddRef();
		return S_OK;
	} else {
		*ppv = 0;
		return E_NOINTERFACE;
	}
}

int DataObjectImpl::LookupFormatEtc(FORMATETC *format) {
	for (int i = 0; i < length; i++) {
		if ((format->tymed & formats[i].tymed) && format->cfFormat == formats[i].cfFormat && format->dwAspect == formats[i].dwAspect) {
			return i;
		}
	}

	return -1;

}
HRESULT __stdcall DataObjectImpl::GetData(FORMATETC *format, STGMEDIUM *stg) {
	int index = LookupFormatEtc(format);
	if (index < 0) {
		return DV_E_FORMATETC ;
	}

	stg->pUnkForRelease = 0;
	stg->tymed = formats[index].tymed;

	if (formats[index].tymed == TYMED_HGLOBAL) {
		stg->hGlobal = DupMem(stgs[index].hGlobal);
	} else {
		return DV_E_TYMED ;
	}
	return S_OK ;
}

HRESULT __stdcall DataObjectImpl::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) {
	return DV_E_FORMATETC ;
}

HRESULT __stdcall DataObjectImpl::QueryGetData(FORMATETC *format) {
	return LookupFormatEtc(format) < 0 ? DV_E_FORMATETC : S_OK ;
}

HRESULT __stdcall DataObjectImpl::GetCanonicalFormatEtc(FORMATETC *formatIn, FORMATETC *formatOut) {
	formatOut->ptd = 0;
	return E_NOTIMPL ;
}

HRESULT __stdcall DataObjectImpl::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) {
	return E_NOTIMPL ;
}

HRESULT __stdcall DataObjectImpl::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnum) {
	if (dwDirection == DATADIR_GET) {
		return CreateEnumFormatEtc(length, formats, ppEnum);
	} else {
		return E_NOTIMPL ;
	}
}

HRESULT __stdcall DataObjectImpl::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) {
	return OLE_E_ADVISENOTSUPPORTED ;
}

HRESULT __stdcall DataObjectImpl::DUnadvise(DWORD dwConnection) {
	return OLE_E_ADVISENOTSUPPORTED ;
}

HRESULT __stdcall DataObjectImpl::EnumDAdvise(IEnumSTATDATA **ppenumAdvise) {
	return OLE_E_ADVISENOTSUPPORTED ;
}

IDataObject * CreateDataObject(FORMATETC *fmtetc, STGMEDIUM *stgmeds, UINT count) {
	return new  DataObjectImpl(fmtetc, stgmeds, count);
}

HGLOBAL StringToHGlobal(const char *s, int len) {
	if(len <=0 || !s){
		return 0;
	}
	HGLOBAL h = GlobalAlloc(GMEM_FIXED, len);
	memcpy(GlobalLock(h), s, len);
	GlobalUnlock(h);
	return h;
}
IDataObject* CreateDataObject(const char* str, int len) {
	FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
	STGMEDIUM stgmed = { TYMED_HGLOBAL, { 0 }, 0 };
	stgmed.hGlobal = StringToHGlobal(str, len);
	return new DataObjectImpl(&fmtetc, &stgmed, 1);
}

}
